<script lang="ts" setup>
import type { ImageInstance } from 'element-plus'
import type { SelectedImage } from '~/composables/imageCropper'
import { Check, CircleCheck, DArrowLeft, DArrowRight, Delete, Download, Refresh, RefreshLeft, RefreshRight, ZoomIn, ZoomOut } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { defineEmits, ref } from 'vue'
import { ImageCropper } from '~/composables/imageCropper'
import eventBus from '~/eventBus'

import { useCropStore } from '~/stores/crop'

// 定义 props
const props = defineProps({
  autoChecked: {
    type: Boolean,
    default: true,
  },
})
// 定义可以触发的事件
const emit = defineEmits(['editImage'])
const cropStore = useCropStore()
const { cropChanged } = cropStore

const cropper = new ImageCropper()

// 组件挂载时监听事件
onMounted(() => {
  eventBus.on('croped-file', handleCrop)
  eventBus.on('download-checked', handleDownloads)
  eventBus.on('compressImageToKB', handleCompressImageToKB)
  eventBus.on('compressImageToPercentage', handleCompressImageToPercentage)
  eventBus.on('compressImageToWidth', handleCompressImageToWidth)
  eventBus.on('compressImageToHeight', handleCompressImageToHeight)
  eventBus.on('cropImageByRatio', handleCropByRatio)
  eventBus.on('compressImageToMaxSide', handleCompressImageToMaxSide)
})

// 组件卸载时移除监听
onUnmounted(() => {
  eventBus.off('croped-file', handleCrop)
  eventBus.off('download-checked', handleDownloads)
  eventBus.off('compressImageToKB', handleCompressImageToKB)
  eventBus.off('compressImageToPercentage', handleCompressImageToPercentage)
  eventBus.off('compressImageToWidth', handleCompressImageToWidth)
  eventBus.off('compressImageToHeight', handleCompressImageToHeight)
  eventBus.off('cropImageByRatio', handleCropByRatio)
  eventBus.off('compressImageToMaxSide', handleCompressImageToMaxSide)
})
// 文件列表
const fileList = ref<SelectedImage[]>([]) // 加载的文件
const editFileUid = ref() // 编辑的图片uid
const srcList = ref<string[]>([]) // 预览列表

function handleCrop(file: SelectedImage) {
  const i = fileList.value.findIndex(file => file.uid === editFileUid.value)
  fileList.value[i] = file
  srcList.value[i] = file.url as string
}

// 当前悬停的文件 UID
const hoveredFile = ref<string | null>(null)

const imageRef = ref<ImageInstance>()

const index = ref(0)
// 预览图片
function handlePreview(_file: SelectedImage) {
  index.value = fileList.value.findIndex(file => file.uid === _file.uid)
  imageRef.value!.showPreview()
}

// 删除文件
function handleRemove(file: SelectedImage) {
  const index = fileList.value.findIndex(f => f.uid === file.uid)
  if (index !== -1) {
    fileList.value.splice(index, 1)
    srcList.value.splice(index, 1)
  }
}

// 文件状态改变时的钩子
function handleChange(file: SelectedImage, files: SelectedImage[]) {
  file.checked = props.autoChecked
  srcList.value.length = 0
  // 为每个文件添加 checked 属性
  files
    .forEach((f) => {
      if (!('checked' in f)) {
        f.checked = false
      }
      srcList.value.push(f.url as string)
    })
  fileList.value = files
}

// 切换文件选中状态
function handleToggleCheck(file: SelectedImage) {
  file.checked = !file.checked
}

function edit(file: SelectedImage) {
  editFileUid.value = file.uid
  emit('editImage', '')
  eventBus.emit('crop-file', file)
}

function editactive(index: number) {
  editFileUid.value = fileList.value[index]!.uid
  emit('editImage', '')
  eventBus.emit('crop-file', fileList.value[index]!)
}

function download(index: number) {
  const file = fileList.value[index]
  const filename = file!.name
  const url = file!.url
  const link = document.createElement('a')
  link.href = url as string
  link.download = filename
  document.body.appendChild(link)
  link.click()
  link.remove()
}

async function handleDownloads() {
  cropper.downloadImages(fileList.value)
}

function updateFileListUrls(list: SelectedImage[]) {
  list.forEach((item) => {
    const index = fileList.value.findIndex(file => file.uid === item.uid)
    if (index !== -1) {
      fileList.value[index] = item // 将 list.url 赋值给 fileList.url
      srcList.value[index] = item.url as string
    }
  })
}

// 处理 compressImageToKB 事件
async function handleCompressImageToKB(value: number) {
  cropChanged()
  const list = await cropper.compressImageToKB(fileList.value, value)
  updateFileListUrls(list)
  cropChanged()
  ElMessage({
    message: '图片处理完成',
    type: 'success',
  })
}

// 处理 compressImageToWidth 事件
async function handleCompressImageToWidth(value: number) {
  cropChanged()
  const list = await cropper.compressImageToWidth(fileList.value, value)
  updateFileListUrls(list)
  cropChanged()
  ElMessage({
    message: '图片处理完成',
    type: 'success',
  })
}

// 处理 compressImageToHeight 事件
async function handleCompressImageToHeight(value: number) {
  cropChanged()
  const list = await cropper.compressImageToHeight(fileList.value, value)
  updateFileListUrls(list)
  cropChanged()
  ElMessage({
    message: '图片处理完成',
    type: 'success',
  })
}

const imageRefs = ref<{ [key: string]: HTMLImageElement }>({}) // 指定类型为对象，键为字符串，值为 HTMLImageElement
function setImageRef(el: Element | null, uid: string) {
  if (el && el instanceof HTMLImageElement) {
    imageRefs.value[uid] = el // 现在可以安全地使用字符串索引
  }
};

// 处理 compressImageToPercentage 事件
async function handleCompressImageToPercentage(value: number) {
  cropChanged()
  const list = await cropper.compressImageToPercentage(fileList.value, value, imageRefs.value)
  updateFileListUrls(list)
  cropChanged()
  ElMessage({
    message: '图片处理完成',
    type: 'success',
  })
}

// 处理 cropImageByRatio 事件
async function handleCropByRatio(ratio: [number, number]) {
  cropChanged()
  const list = await cropper.cropImageByRatio(fileList.value, ratio, imageRefs.value)

  updateFileListUrls(list)
  cropChanged()
  ElMessage({
    message: '图片处理完成',
    type: 'success',
  })
}

// 添加 compressImageToMaxSide 事件监听和处理
async function handleCompressImageToMaxSide(maxSide: number) {
  cropChanged() // 添加 cropChanged 调用
  const list = await cropper.compressImageToMaxSide(fileList.value, maxSide)
  updateFileListUrls(list)
  cropChanged()
  ElMessage({
    message: '图片处理完成',
    type: 'success',
  })
}

function formatFileSize(fileSize: number): string {
  const units = ['B', 'KB', 'MB', 'GB', 'TB']
  const index = Math.floor(Math.log(fileSize) / Math.log(1024))
  return `${(fileSize / 1024 ** index).toFixed(2)} ${units[index]}`
}
</script>

<template>
  <div class="text-center">
    <el-image
      ref="imageRef" alt="" class="file-image" fit="contain" :initial-index="index" :preview-src-list="srcList"
      style="height: 0;"
    >
      <template #toolbar="{ actions, prev, next, reset, activeIndex }">
        <ElIcon @click="prev">
          <DArrowLeft />
        </ElIcon>
        <ElIcon @click="next">
          <DArrowRight />
        </ElIcon>
        <ElIcon @click="actions('zoomOut')">
          <ZoomOut />
        </ElIcon>
        <ElIcon @click="actions('zoomIn', { enableTransition: false, zoomRate: 2 })">
          <ZoomIn />
        </ElIcon>
        <ElIcon
          @click="
            actions('clockwise', { rotateDeg: 180, enableTransition: false })
          "
        >
          <RefreshRight />
        </ElIcon>
        <ElIcon @click="actions('anticlockwise')">
          <RefreshLeft />
        </ElIcon>
        <ElIcon @click="reset">
          <Refresh />
        </ElIcon>
        <ElIcon @click="editactive(activeIndex)">
          <span i-carbon:crop />
        </ElIcon>
        <ElIcon @click="download(activeIndex)">
          <Download />
        </ElIcon>
      </template>
    </el-image>
    <!-- 文件上传组件 -->
    <el-upload
      action="#" list-type="picture-card" :on-change="handleChange" :file-list="fileList" :auto-upload="false"
      multiple :on-remove="handleRemove" class="pl-4"
    >
      <span i-carbon:add-large text-5xl />

      <!-- 自定义文件渲染 -->
      <template #file="{ file }">
        <div
          :class="{ 'selected-border': file.checked }" class="custom-file-item flex justify-center" @mouseenter="hoveredFile = file.uid"
          @mouseleave="hoveredFile = null"
        >
          <img
            :ref="(el) => el && setImageRef(el as HTMLImageElement, file.uid)" :src="file.url" alt="file.name"
            class="file-image" uid="file.uid"
          >
          <!-- 遮罩层 -->
          <div v-if="hoveredFile === file.uid" class="mask-layer">
            <div class="text-xs text-gray-300 pl-2 pt-1 text-left w-full whitespace-nowrap text-ellipsis overflow-hidden">
              {{ file.name }}
            </div>
            <div class="p4 flex w-[80%] justify-between">
              <el-icon class="icon" @click="handleToggleCheck(file)">
                <Check v-if="file.checked" />
                <CircleCheck v-else />
              </el-icon>
              <el-icon class="icon" @click="handlePreview(file)">
                <ZoomIn />
              </el-icon>
              <el-icon class="icon" @click="edit(file)">
                <span i-carbon:crop />
              </el-icon>
              <el-icon class="icon" @click="handleRemove(file)">
                <Delete />
              </el-icon>
            </div>
            <div class="text-xs text-gray-300 pb-1 pr-2 text-right w-full">
              {{ formatFileSize(file.size as number) }}
            </div>
          </div>
        </div>
      </template>
    </el-upload>
  </div>
</template>

<style scoped>
.custom-file-item {
  position: relative;
  width: 100%;
}
.file-image {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.mask-layer {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  /* 半透明遮罩 */
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  opacity: 0;
  transition: opacity 0.3s;
}

.custom-file-item:hover .mask-layer {
  opacity: 1;
  /* 鼠标悬停时显示遮罩 */
}

.icon {
  color: white;
  font-size: 20px;
  cursor: pointer;
  transition: color 0.3s;
}

.icon:hover {
  color: #409eff;
  /* 鼠标悬停时图标颜色 */
}

.selected-marker {
  position: absolute;
  top: 5px;
  right: 5px;
  background-color: #409eff;
  color: white;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
}

.selected-border {
  border: 2px solid var(--el-color-primary);
  border-radius: 6px;
}
</style>
